/*    

    bobbietalk - a visual programming environment for toy robotics

    Copyright (C) 2013  Roberto Tiella

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/
package bobbietalk.gui;

import java.awt.Component;
import java.awt.Container;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Rectangle2D;
import java.io.PrintWriter;
import java.util.ResourceBundle;

/**
 *
 * @author roberto
 */
public class ProgrammingBlock extends JigsawPuzzlePieceButton {

    final protected ResourceBundle bundle = java.util.ResourceBundle.getBundle("bobbietalk/Labels");
    private ProgrammingBoard board;
    private ProgrammingBlock nextBlock;
    private ProgrammingBlock previousBlock;
    private boolean engaged;
    private ProgrammingBlock lastEngaged;
    private boolean nextFemale;
    private boolean nextMale;
    private String typeLabel;

    public ProgrammingBlock() {
    }

    public String getType() {
        return this.getClass().getSimpleName();
    }

    public void setType(String type) {
        // do nothing for this basic class
    }

    public void setBoard(ProgrammingBoard board) {
        this.board = board;
    }

    public String getTypeLabel() {
        return typeLabel;
    }

    public void setTypeLabel(String typeLabel) {
        this.typeLabel = typeLabel;
    }

    public void initListeners() {

        // setBounds(0, 0, 130, 100);

//        addActionListener(new ActionListener() {
//
//            @Override
//            public void actionPerformed(ActionEvent e) {
//                System.out.println("something happened");
//            }
//        });


        addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent e) {
//                System.out.println("mouseClicked:"+e);
            }

            @Override
            public void mousePressed(MouseEvent e) {
//                System.out.println("mousePressed:"+e);
                handleAttentionGained();
            }

            @Override
            public void mouseReleased(MouseEvent e) {

                if (lastEngaged != null) {

                    Rectangle others = lastEngaged.getBounds();
                    Rectangle bounds = getBounds();

                    int oldx = bounds.x;
                    int oldy = bounds.y;

                    bounds.y = others.y;
                    bounds.x = others.x + others.width - d - 4;

                    // setPosition(bounds.x,bounds.y);

                    propagateMotion(true, bounds.x - oldx, bounds.y - oldy);

                    lastEngaged.setEngaged(false);
                    lastEngaged.setHighlighted(false);
                    lastEngaged.repaint();

                    lastEngaged.setNextBlock(ProgrammingBlock.this);
                    setPreviousBlock(lastEngaged);

                    lastEngaged = null;

                    board.setProgramChanged(true);
                    board.fireProgramChangedEvent();

                }


                board.changed(ProgrammingBlock.this);

            }

            @Override
            public void mouseEntered(MouseEvent e) {
//            System.out.println("mouseEntered:"+e);    
            }

            @Override
            public void mouseExited(MouseEvent e) {
//            System.out.println("mouseExited:"+e);    
            }
        });

        addMouseMotionListener(new MouseMotionListener() {
            int relx;
            int rely;

            @Override
            public void mouseDragged(MouseEvent e) {
                // System.out.println("mouseDragged:"+e); 

                int oldx = getX();
                int oldy = getY();

                int x = e.getX();
                int y = e.getY();

                int dx = getX() - relx;
                int dy = getY() - rely;

                int newx = dx + x;
                int newy = dy + y;

                Rectangle r = getBounds();

                r.x = newx;
                r.y = newy;

                setPosition(r.x, r.y);

                if (previousBlock != null) {
                    lastEngaged = previousBlock;
                    previousBlock.setEngaged(true);
                    previousBlock.setHighlighted(true);
                    previousBlock.releaseNext(this);
                    previousBlock.repaint();
                    previousBlock = null;
                    board.setProgramChanged(true);
                    board.fireProgramChangedEvent();
                }

                propagateMotion(false, newx - oldx, newy - oldy);

                handleCloseObjects();


            }

            @Override
            public void mouseMoved(MouseEvent e) {

                relx = e.getX();
                rely = e.getY();

            }
        });

//        addFocusListener(new FocusListener() {
//            @Override
//            public void focusGained(FocusEvent e) {
//
//                handleAttentionGained();
//
//            }
//
//            @Override
//            public void focusLost(FocusEvent e) {
//            }
//        });


    }

    private void handleAttentionGained() {

        if (!isSelected()) {

            setSelected(true);
            board.setSelected(this);

        }

    }

    private void handleCloseObjects() {

        Container parent = this.getParent();

        for (int i = 0; i < parent.getComponentCount(); i++) {

            final Component other = parent.getComponent(i);

            if (other != this && this.nextBlock != other && other instanceof ProgrammingBlock) {

                ProgrammingBlock anotherBlock = (ProgrammingBlock) parent.getComponent(i);

                if (anotherBlock.checkIsCloseTo(this)) {

                    lastEngaged = anotherBlock;
                    break;

                } else {
                    lastEngaged = null;
                }

            }

        }

    }

    public void setPosition(int x, int y) {

        Rectangle r = new Rectangle(x, y, getWidth(), getHeight());

        updatePosition(r);
    }

    private boolean checkIsCloseTo(ProgrammingBlock other) {

        Rectangle mine = this.getBounds();
        Rectangle others = other.getBounds();

//        System.out.println("mine:" + mine);
//        System.out.println("others:" + others);

        mine.grow(20, 20);
        others.grow(20, 20);

        Rectangle2D inter = mine.createIntersection(others);

//        System.out.println("inter:" + inter);

        if (isNextMale() && getNextBlock() == null && other.isNextFemale()
                && inter.getWidth() > 0 && inter.getHeight() > 0) {

            if (!engaged) {

                if (inter.getWidth() > 55) {

//                    System.out.println("engaged");

                    engaged = true;
//                    other.otherIsEngaged(this, true);

                    setHighlighted(true);

                    this.repaint();

                }
            }

            if (engaged) {

//            System.out.println("intersection:" + inter);

                if (inter.getWidth() < 45) {

//                    System.out.println("disengaged");

                    engaged = false;
//                    other.otherIsEngaged(this, false);
                    setHighlighted(false);

                    this.repaint();

                }
            }

        } else {
            if (engaged) {
                engaged = false;
                setHighlighted(false);
                this.repaint();
            }
        }

        return engaged;

    }

    private void otherIsEngaged(ProgrammingBlock ablock, boolean b) {

        if (b) {
            lastEngaged = ablock;
        } else {
            lastEngaged = null;
        }

    }

    private void setEngaged(boolean b) {
        engaged = b;
    }

    public ProgrammingBlock getNextBlock() {
        return nextBlock;
    }

    public void setNextBlock(ProgrammingBlock nextBlock) {
        this.nextBlock = nextBlock;
    }

    public ProgrammingBlock getPreviousBlock() {
        return previousBlock;
    }

    public void setPreviousBlock(ProgrammingBlock previousBlock) {
        this.previousBlock = previousBlock;
    }

    public void toBedeleted() {

        if (previousBlock != null) {
            previousBlock.setNextBlock(null);
        }

        if (nextBlock != null) {
            nextBlock.setPreviousBlock(null);
        }

        setNextBlock(null);
        setPreviousBlock(null);

    }

    private boolean isNextFemale() {

        return nextFemale;

    }

    public void setNextFemale(boolean nextFemale) {
        this.nextFemale = nextFemale;
    }

    public boolean isNextMale() {
        return nextMale;
    }

    public void setNextMale(boolean nextMale) {
        this.nextMale = nextMale;
    }

    public void dump(PrintWriter pw) {

        pw.println("name: " + getName());
        pw.println("class: " + getClass().getSimpleName());
        pw.println("type: " + getType());
        pw.println("next: " + getNextBlock());
        pw.println("previous:" + getPreviousBlock());

    }

    public String toString() {

        StringBuilder sb = new StringBuilder();
        sb.append(getClass().getSimpleName());
        sb.append(':').append(getName());
        return sb.toString();
    }

    private void releaseNext(MouseMotionListener aThis) {
        nextBlock = null;
    }

    private void releasePrevious(MouseMotionListener aThis) {

        previousBlock = null;
    }

    public void propagateMotion(boolean moveYourself, int dx, int dy) {

        int newx = getX() + dx;
        int newy = getY() + dy;

        if (moveYourself) {
            setBounds(newx, newy, getWidth(), getHeight());
        }

        if (nextBlock != null) {
            nextBlock.propagateMotion(true, dx, dy);
        }

    }

    private void updatePosition(Rectangle bounds) {
        setBounds(bounds);
    }

    public void serializeAttributes(StringBuilder buffer) {

        buffer.append("class=");
        buffer.append(getClass().getSimpleName());
        buffer.append(";\n");

        buffer.append("type=");
        buffer.append(getType());
        buffer.append(";\n");


        buffer.append("name=");
        buffer.append(getName());
        buffer.append(";\n");

        buffer.append("x=");
        buffer.append(getX());
        buffer.append(";\n");

        buffer.append("y=");
        buffer.append(getY());
        buffer.append(";\n");

        buffer.append("prev=");
        if (getPreviousBlock() != null) {
            buffer.append(getPreviousBlock().getName());
        } else {
            buffer.append("null");
        }
        buffer.append(";\n");

        buffer.append("next=");
        if (getNextBlock() != null) {
            buffer.append(getNextBlock().getName());
        } else {
            buffer.append("null");
        }

        buffer.append(";\n");
    }

    public void update() {
    }

    public void setAttribute(String string, String string0) {
        // nothing to do in the defaut implementation
    }

    public boolean isType(String type) {
        return getType().equals(type);
    }
}
